package com.qiuguo.rtsp.service;

import cn.hutool.json.JSONUtil;
import com.github.kokorin.jaffree.ffmpeg.*;
import com.qiuguo.rtsp.utils.PropertyValueUtils;
import com.qiuguo.rtsp.utils.IPConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.io.IOException;
import java.net.InetAddress;
import java.nio.file.Paths;
import java.util.concurrent.*;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

@Slf4j
@Service
public class StreamService {

    public static final String OUTPUT_DIR = System.getProperty("user.home") + "/hls";
    private final Map<String, StreamTask> runningTasks = new ConcurrentHashMap<>();
    private final ExecutorService executorService = Executors.newCachedThreadPool();
    
    // 服务器端口
    @Value("${server.port:8080}")
    private int serverPort;
    
    // 服务器IP地址
    private String serverIp;
    
    @PostConstruct
    public void init() {
        // 获取服务器IP地址
        try {
            serverIp = new IPConverter().getIpv4();
            log.info("服务器IP地址: {}", serverIp);
            log.info("服务器端口: {}", serverPort);
        } catch (Exception e) {
            log.error("获取服务器IP地址失败", e);
            serverIp = "127.0.0.1";
        }
    }

    public void startStreaming() {
        try {
            log.info("创建目录: {}", OUTPUT_DIR);
            Process mkdir = Runtime.getRuntime().exec("mkdir -p " + OUTPUT_DIR);
            mkdir.waitFor();
        } catch (IOException | InterruptedException e) {
            log.error("创建目录失败", e);
        }
        startStreaming("stream", PropertyValueUtils.getRtspUrl());
    }


    /**
     * 启动指定文件名的流转换任务
     *
     * @param taskId 文件名（不包含扩展名）
     * @param rtspUrl  RTSP 流地址
     * @return HLS 流地址
     */
    public String startStreaming(String taskId, String rtspUrl) {
        // 如果任务已在运行，直接返回地址
        if (runningTasks.containsKey(taskId) && 
            (runningTasks.get(taskId).getStatus() == StreamTask.TaskStatus.RUNNING || 
             runningTasks.get(taskId).getStatus() == StreamTask.TaskStatus.PROBING)) {
            log.info("任务 {} 已在运行，返回现有地址", taskId);
            return getHlsUrl(taskId);
        }

        // 启动新任务
        log.info("开始启动任务:OUTPUT_DIR: {}, 任务: {}，RTSP地址: {}", OUTPUT_DIR, taskId, rtspUrl);
        String hlsOutput = OUTPUT_DIR + "/" + taskId + ".m3u8";

        // 监控任务完成情况
        executorService.submit(() -> {
            StreamTask streamTask = new StreamTask(taskId, OUTPUT_DIR, rtspUrl); // 创建任务对象时传入RTSP地址
            runningTasks.put(taskId, streamTask);
            while (streamTask.getStatus() != StreamTask.TaskStatus.STOPPED) {
                try {
                    log.info("任务 {} 正在运行，等待转换完成... status: {}", taskId, streamTask.getStatus());
                    // 检查 RTSP URL 是否可达
                    if (!isRtspUrlReachable(rtspUrl)) {
                        log.error("RTSP URL 不可达: {}", rtspUrl);
                        // 保持探测状态
//                        streamTask.setStatus(StreamTask.TaskStatus.PROBING);
                        Thread.sleep(1000L);
                        continue;
                    }
                    
                    // 设置为运行状态
                    streamTask.setRunning();
                    
                    FFmpegResultFuture ffmpegResultFuture = FFmpeg.atPath()
                            .addInput(UrlInput.fromUrl(rtspUrl))
                            .setOverwriteOutput(true)
                            .addArguments("-rtsp_transport", "tcp")
                            .addArguments("-c:v", "copy")
                            .addArguments("-c:a", "aac")
                            .addArguments("-f", "hls")
                            .addArguments("-hls_time", "1")
                            .addArguments("-hls_list_size", "1")
                            .addArguments("-hls_flags", "delete_segments")
                            .addArguments("-hls_segment_filename", Paths.get(OUTPUT_DIR, taskId + "_segment_%03d.ts").toString())
                            .addOutput(UrlOutput.toPath(Paths.get(hlsOutput)))
                            .executeAsync();

                    // 更新任务对象中的future
                    streamTask.setFuture(ffmpegResultFuture);
                    FFmpegResult result = ffmpegResultFuture.get();
                    log.info("任务 {} 执行完成，退出码: {}", taskId, JSONUtil.toJsonStr(result));
                } catch (InterruptedException e) {
                    log.error("任务 {} 被中断", taskId, e);
                    streamTask.cleanupSegmentFiles();
                    break;
                } catch (ExecutionException e) {
                    log.error("任务 {} 执行异常", taskId, e);
                    streamTask.cleanupSegmentFiles();
                } catch (Exception e) {
                    log.error("任务 {} 发生未知异常", taskId, e);
                    streamTask.cleanupSegmentFiles();
                }
            }
            runningTasks.remove(taskId);
        });

        return getHlsUrl(taskId);
    }
    
    /**
     * 获取当前运行的任务列表，包含任务状态和RTSP地址
     *
     * @return 任务列表信息
     */
    public Map<String, TaskInfo> getRunningTaskInfo() {
        return runningTasks.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> new TaskInfo(
                                entry.getValue().getStatus(),
                                entry.getValue().getRtspUrl(),
                                getHlsUrl(entry.getKey())
                        )
                ));
    }
    
    /**
     * 获取当前运行的任务列表（仅状态）
     *
     * @return 任务列表，键为任务ID，值为是否正在运行
     */
    public Map<String, Boolean> getRunningTasks() {
        return runningTasks.entrySet().stream()
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        entry -> entry.getValue().getStatus() != StreamTask.TaskStatus.STOPPED
                ));
    }

    /**
     * 检查 RTSP URL 是否可达
     *
     * @param rtspUrl RTSP 流地址
     * @return 是否可达
     */
    private boolean isRtspUrlReachable(String rtspUrl) {
        try {
            log.info("正在探测 RTSP URL: {}", rtspUrl);
            // 解析 RTSP URL 获取主机和端口
            java.net.URI uri = new java.net.URI(rtspUrl);
            String host = uri.getHost();
            int port = uri.getPort();
            
            // 如果端口未指定，使用 RTSP 默认端口 554
            if (port == -1) {
                port = 554;
            }
            
            // 创建 socket 并尝试连接
            try (java.net.Socket socket = new java.net.Socket()) {
                log.info("正在尝试连接: {}:{}", host, port);
                socket.connect(new java.net.InetSocketAddress(host, port), 5000); // 5秒超时
                log.info("RTSP URL 探测成功: {}:{}", host, port);
                return true;
            }
        } catch (Exception e) {
            log.error("RTSP URL 探测失败: {}", rtspUrl, e);
            return false;
        }
    }

    /**
     * 获取 HLS 流地址（完整URL）
     *
     * @param taskId 文件名（不包含扩展名）
     * @return HLS 流地址
     */
    public String getHlsUrl(String taskId) {
        return "http://" + serverIp + ":" + serverPort + "/hls/" + taskId + ".m3u8";
    }

    /**
     * 停止指定文件名的流转换任务
     *
     * @param taskId 文件名（不包含扩展名）
     * @return 是否成功停止任务
     */
    public boolean stopStreaming(String taskId) {
        log.info("尝试停止任务: {}", taskId);

        // 检查任务是否存在
        if (!runningTasks.containsKey(taskId)) {
            log.warn("任务 {} 不存在或已停止", taskId);
            return false;
        }
        // 修改运行状态为停止
        StreamTask streamTask = runningTasks.get(taskId);
        boolean stop = streamTask.stop();
        // 从运行列表中移除
        runningTasks.remove(taskId);

        log.info("任务 {} 已停止，清理完成", taskId);
        return stop;
    }



    /**
     * 关闭所有正在运行的任务
     */
    @PreDestroy
    public void shutdown() {
        log.info("正在关闭所有流转换任务...");
        runningTasks.entrySet().forEach(entry -> {
            String fileName = entry.getKey();
            StreamTask streamTask = entry.getValue();
            if (streamTask.getStatus() != StreamTask.TaskStatus.STOPPED) {
                stopStreaming(fileName);
            }
        });
        
        executorService.shutdown();
        try {
            if (!executorService.awaitTermination(5, TimeUnit.SECONDS)) {
                executorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            executorService.shutdownNow();
            Thread.currentThread().interrupt();
        }
        log.info("所有流转换任务已关闭");
    }
    
    /**
     * 任务信息类
     */
    public static class TaskInfo {
        private StreamTask.TaskStatus status;
        private String rtspUrl;
        private String hlsUrl;
        
        public TaskInfo(StreamTask.TaskStatus status, String rtspUrl, String hlsUrl) {
            this.status = status;
            this.rtspUrl = rtspUrl;
            this.hlsUrl = hlsUrl;
        }
        
        // Getters
        public StreamTask.TaskStatus getStatus() { return status; }
        public String getRtspUrl() { return rtspUrl; }
        public String getHlsUrl() { return hlsUrl; }
    }
    
    // Getters for server info
    public String getServerIp() {
        return serverIp;
    }
    
    public int getServerPort() {
        return serverPort;
    }
}